#include "PlayState.h"

#include "ExplosiveBarrel.h"
#include "ODN_PropEntity.h"
#include "ODN_EntityEvents.h"

#include <cassert>

#include <fstream>

using ouden::CGameMgr;

using namespace ouden;

template<>
CPlayState* ouden::CSingleton<CPlayState>::m_pInstance = 0;

CPlayState::CPlayState()
{
	//ctor
	
}

CPlayState::~CPlayState()
{
	//dtor
}

void CPlayState::onEnter()
{
	
	ODN_LOG(ouden::LML_DEBUG) << "Entering play state" << std::endl;
	
	//**Register all play state entities here**
	REGISTER_ENTITY_CLASS("ExplosiveBarrel", CExplosiveBarrel);
	
	
	//REGISTER_ENTITY_CLASS("Prop", ouden::CPropEntity);
	
	//*****************************************
	
	//Initialise the exit variable to false
	m_bExit = false;
	
	//CGameMgr::getInstance()->beginClientConnection("127.0.0.1", 60002, 60003);
	
	//ouden::CLogMgr::getInstance()->log(ouden::LML_NORMAL) 
	//		<< "This is a test" << std::endl << std::endl;
	
	//Create the scene
	createScene();
	
}

class CScreenShotEvent : public ouden::CLocalEvent
{
	
public:
	
	virtual void onActivate()
	{
		
		ouden::CGameMgr::getInstance()->getOgreWindow()->
			writeContentsToTimestampedFile("screenshot", ".jpg");
		
	}
	
};

void CPlayState::createScene()
{
	
	ODN_LOG(ouden::LML_DEBUG) << "Creating play state scene" << std::endl;
	
	//Set the playing plane (it's a 2D game after all) to lie at z=0 with the 
	// normal facing the camera
	m_plPlayingPlane.d = 0;
	m_plPlayingPlane.normal = Vector(0.0, 0.0, -1.0);
	
	CGameMgr::getInstance()->getSceneMgr()->setAmbientLight(Ogre::ColourValue(0.3, 0.3, 0.3));
	
	Ogre::Light* pLight1 = CGameMgr::getInstance()->getSceneMgr()->createLight("Light1");
	pLight1->setPosition(0, 3, 3);
	pLight1->setType(Ogre::Light::LT_POINT);
	pLight1->setDiffuseColour(1.0, 1.0, 1.0);
	pLight1->setSpecularColour(1.0, 1.0, 1.0);
	
	CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_F12, new CScreenShotEvent() );
			
			
	CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_SPACE, new ouden::CCreateModelEntityEvent("Cube.xml", Vector(0, 0, 0)) );
	
	//CModelEntity* pModel1 = static_cast<ouden::CModelEntity*>(CGameMgr::getInstance()->
	//		getGameWorld()->createEntity("ModelEntity", "Model1"));
			
	//pModel1->loadModel("Cube.xml");
	
	//pModel1->getOgreEntity()->setMaterialName("Plastic/Blue");
	//pModel1->getOgreEntity()->setVisible(true);
	//pModel1->getOgreEntity()->setNormaliseNormals(true); // Keep the normals normalized even after scaling.
	
	/*
	ouden::CPropEntity* pProp1 = static_cast<ouden::CPropEntity*>(CGameMgr::getInstance()->
			getGameWorld()->createEntity("Prop", "TheBox"));
			
	ouden::CPropEntity* pProp2 = static_cast<ouden::CPropEntity*>(CGameMgr::getInstance()->
			getGameWorld()->createEntity("Prop"));
			
	pProp2->getSolid()->setStatic(true);
	pProp2->getSolid()->setPosition(0.6, -2.0, 0.0);
	
	ouden::CPropEntity* pProp3 = static_cast<ouden::CPropEntity*>(CGameMgr::getInstance()->
			getGameWorld()->createEntity("Prop"));
			
	pProp3->getSolid()->setStatic(true);
	pProp3->getSolid()->setPosition(-0.6, -2.0, 0.0);
	
	//Code to create all the scene entities
	
	CExplosiveBarrel* pBarrel1 = static_cast<CExplosiveBarrel*>(CGameMgr::getInstance()->
			getGameWorld()->createEntity("ExplosiveBarrel"));
			
	CGameMgr::getInstance()->
			getGameWorld()->createEntity("ExplosiveBarrel");
			
	CGameMgr::getInstance()->
			getGameWorld()->createEntity("ExplosiveBarrel");
			
	ouden::CBaseEntity* pEnt1 = CGameMgr::getInstance()->
			getGameWorld()->createEntity("ExplosiveBarrel");
	
	CExplosiveBarrel* pBarrel2 = static_cast<CExplosiveBarrel*>(CGameMgr::getInstance()->
			getGameWorld()->findEntity("aPooNamed"));
	
	if (pEnt1) {
		std::cout <<std::endl;
		std::cout << pEnt1->getName() << std::endl;
		std::cout <<std::endl;
	}
	
	if (pBarrel1) {
		
		//TODO: Eventually when have player entity, use this to bind the 
		//	movement keys to direction vectors
		
		//Key down events
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_LEFT, new ouden::C_StartMoveEvent(pBarrel1, Vector(-1, 0, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_RIGHT, new ouden::C_StartMoveEvent(pBarrel1, Vector(1, 0, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_UP, new ouden::C_StartMoveEvent(pBarrel1, Vector(0, 1, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyDownToEvent(
			Ogre::KC_DOWN, new ouden::C_StartMoveEvent(pBarrel1, Vector(0, -1, 0)) );
		
		//Key up events
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyUpToEvent(
			Ogre::KC_LEFT, new ouden::C_EndMoveEvent(pBarrel1, Vector(-1, 0, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyUpToEvent(
			Ogre::KC_RIGHT, new ouden::C_EndMoveEvent(pBarrel1, Vector(1, 0, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyUpToEvent(
			Ogre::KC_UP, new ouden::C_EndMoveEvent(pBarrel1, Vector(0, 1, 0)) );
		CGameMgr::getInstance()->getInputBindingMgr()->bindKeyUpToEvent(
			Ogre::KC_DOWN, new ouden::C_EndMoveEvent(pBarrel1, Vector(0, -1, 0)) );
		
		//ouden::CBreakEvent breakEvent(pBarrel1);
		
	}
	*/
	//std::cout <<std::endl;
	//std::cout << pBarrel1->getName() << std::endl;
	//std::cout <<std::endl;
	/*
	Ogre::Entity* pBoxEntity = CGameMgr::getInstance()->getSceneMgr()->createEntity("box1", "cube.mesh");
	
	pBoxEntity->setMaterialName("Plastic/Red");
	
	Ogre::SceneNode* pBoxNode = CGameMgr::getInstance()->getSceneMgr()->getRootSceneNode()->
				createChildSceneNode("box1");
	
	pBoxNode->translate(0, 0, -0.5);
	pBoxNode->scale(0.1, 0.1, 0.1);
	
	pBoxNode->attachObject(pBoxEntity);
	
	std::cout << "CGameMgr::getInstance() = " << CGameMgr::getInstance() << std::endl;
	std::cout << "CGameMgr::getInstance()->getSceneMgr() = " << CGameMgr::getInstance()->getSceneMgr() << std::endl;
	
	std::cout << "before new entity" << std::endl;
	//TODO: Remove this or replace with createEntity
	//ouden::CModelEntity* pModel = new ouden::CModelEntity("model1", "cube");
	std::cout << "after new entity" << std::endl;
	//delete pModel;
	*/
	
	//=========Fade Rectangle Setup=========
	//Create the fade rectangle with texture coordinates
	/*m_pFadeRect = new Ogre::Rectangle2D(true);
	
	//Cover the whole screen
	m_pFadeRect->setCorners(0.0, 0.0, 1.0, 1.0);
	
	//Set the material
	m_pFadeRect->setMaterial("Plastic/Green");
	
	//Render the fade rectangle after everything else
	m_pFadeRect->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
	
	// Attach the fade rectangle to the scene
	Ogre::SceneNode* pFadeRectSceneNode = CGameManager::getInstance()->getSceneMgr()->getRootSceneNode()->createChildSceneNode("FadeRect");
	pFadeRectSceneNode->attachObject(m_pFadeRect);
	pFadeRectSceneNode->translate(0, 0, -0.5);
	pFadeRectSceneNode->scale(0.1, 0.1, 0.1);*/
	
	m_pCursorNode = CGameMgr::getInstance()->getSceneMgr()->getRootSceneNode()->
				createChildSceneNode("cursor");
	
	//m_pCursorRect = new Ogre::Rectangle2D(true);
	//m_pCursorRect->setCorners(-0.1, 0.1, 0.1, -0.1);
	
	// Create background material
	Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create("Cursor", "General");
	//material->getTechnique(0)->getPass(0)->createTextureUnitState("rockwall.tga");
	material->getTechnique(0)->setAmbient(0, 0, 1/*, 0.5*/);
	material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
	material->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
	
	// Disable lighting on the background (it will show as fully lit)
	material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
	
	//m_pCursorRect->setBoundingBox(Ogre::AxisAlignedBox(-100000.0*Ogre::Vector3::UNIT_SCALE, 100000.0*Ogre::Vector3::UNIT_SCALE));
	//m_pCursorRect->setMaterial("Cursor");
	//m_pCursorRect->setVisible(true);
	
	//Render the fade rectangle after everything else
	//m_pCursorRect->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
	
	Ogre::Entity* pCursorEntity = CGameMgr::getInstance()->getSceneMgr()->
									createEntity("cursor", "sphere.mesh");
	
	pCursorEntity->setMaterialName("Plastic/Blue");
	
	m_pCursorNode->attachObject(pCursorEntity);
	
	m_pCursorNode->translate(0.4, 0, -0.5);
	m_pCursorNode->scale(0.1, 0.1, 0.1);
	
}

void CPlayState::onExit()
{
	
	//Delete the cursor rectangle
	//if (m_pCursorRect)
	//	delete m_pCursorRect;
	
	CGameMgr::getInstance()->getSceneMgr()->clearScene();
	
}

void CPlayState::onKeyDown(ouden::KeyCode iKey)
{
	
	//Check for the pause key
	if (Ogre::KC_P == iKey) {
		
		//Push the pause state onto the stack
		//pushState(CPauseState::getInstance());
		
	}
	else if (Ogre::KC_ESCAPE == iKey) {
		
		m_bExit = true;
		
	}
	
}

void CPlayState::onMouseMove(ouden::Vector vPos, ouden::MouseButton iButtonState)
{
	
	//Get a ray from the cursor's screen position through the camera and into the world
	Ogre::Ray rCursorRay = CGameMgr::getInstance()->getMainCamera()->
							getCameraToViewportRay(vPos[0], vPos[1]);
	
	//Intersect the cursor ray with the playing plane
	std::pair< bool, Ogre::Real > paIntersection = rCursorRay.intersects(m_plPlayingPlane);
	
	//Make sure that the cursor ray did intersect the playing plane, which it 
	// definetly should
	if (!paIntersection.first) {
		//TODO: Work out why logging is crashing program
		ODN_LOG(ouden::LML_ERROR) << "Cursor ray did not collide with playing plane. Aim not updated.";
		return;
	}
	
	//Get the distance along the ray that the intersection occurs, given by the
	// second element of the returned pair
	Scalar t = paIntersection.second;
	
	//Find the point of intersection
	Ogre::Vector3 vCursorWorldPos = rCursorRay.getPoint(t);
	
	m_pCursorNode->setPosition(vCursorWorldPos[0], vCursorWorldPos[1], 0.0);
	
	/*ouden::CPropEntity* pTheBox = static_cast<ouden::CPropEntity*>(ouden::CGameMgr::getInstance()->getGameWorld()->findEntity("TheBox"));
	
	opal::Force fForce;
	Ogre::Vector3 temp = vCursorWorldPos - ouden::toOudenVector(pTheBox->getSolid()->getPosition());
	//std::cout << temp[0] << std::endl;
	//std::cout << temp[1] << std::endl;
	
	fForce.vec = opal::Vec3r(temp[0],temp[1],temp[2]);//ouden::Vector3D(temp*10);
	fForce.duration = 0.0156;
	//fForce.pos = pTheBox->getSolid()->getPosition();
	fForce.type = opal::GLOBAL_FORCE;
	pTheBox->getSolid()->addForce(fForce);
	*/
	//TODO: Consider binding this all as an event to onMouseMove
	//TODO: Create event and send to player
	
}

void CPlayState::onUnBufInput(ouden::IInputDevice* pInput)
{
	
	/*
	class CMSG_ExplosionHit : public IMessage
	{
	
	public:
		
		CMSG_ExplosionHit(Vector vForce, Scalar fFireDamage)
			 : m_vForce(vForce), m_fFireDamage(fFireDamage) 		{}
		
		virtual void send(CMessageReceiver* pReceiver)
		{
			pReceiver->processMessage(this);
		}
		
		const Vector getForce() 		{ return m_vForce; }
		const Scalar getFireDamage() 	{ return m_fFireDamage; }
		
	private:
		
		Vector m_vForce;
		Scalar m_fFireDamage;
		
	};
	
	CMSG_ExplosionHit msgExplosion(Vector(1, 1), 1);
	
	CEntity e = m_pGameWorld->getEntity("player");
	msgExplosion.send(e);
	*/
	
	ouden::Vector vCamMovement;
	
	const ouden::Scalar fMoveSpeed = 0.0005;
	
	if (pInput->isKeyDown(Ogre::KC_LEFT)
		|| pInput->isKeyDown(Ogre::KC_A))
	{
		// Move camera left.
		vCamMovement[0] -= (fMoveSpeed);
		
	}

	if (pInput->isKeyDown(Ogre::KC_RIGHT)
		|| pInput->isKeyDown(Ogre::KC_D))
	{
		// Move camera left.
		vCamMovement[0] += (fMoveSpeed);
		
	}
	
	if (pInput->isKeyDown(Ogre::KC_UP)
		|| pInput->isKeyDown(Ogre::KC_W))
	{
		// Move camera left.
		vCamMovement[2] -= (fMoveSpeed);
		
	}

	if (pInput->isKeyDown(Ogre::KC_DOWN)
		|| pInput->isKeyDown(Ogre::KC_S))
	{
		// Move camera left.
		vCamMovement[2] += (fMoveSpeed);
		
	}
	
	//TODO: Conversion for vectors
	CGameMgr::getInstance()->getMainCamera()->moveRelative(vCamMovement);
	
	ouden::Angle aRotX = pInput->getMouseRel()[0] * -0.13;
	ouden::Angle aRotY = pInput->getMouseRel()[1] * -0.13;
	
	//CGameMgr::getInstance()->getMainCamera()->yaw(Ogre::Degree(aRotX));
	//CGameMgr::getInstance()->getMainCamera()->pitch(Ogre::Degree(aRotY));
	
	const ouden::Scalar fCursorMotionScale = 0.01;
	
	//std::cout << pInput->getMouseRel()[0] << std::endl;
	
	//m_pCursorNode->translate(pInput->getMouseRel()[0] * fCursorMotionScale, pInput->getMouseRel()[1] * fCursorMotionScale, 0);
	
	//TODO: Put code for creating a CAimMoveLEvent with the current mouse cursor position
	
}

bool CPlayState::onTick(ouden::CGameTimer* pTimer)
{
	
	assert(pTimer);
	
	ouden::CGameMgr::getInstance()->getSimulator()->simulate(0.015625);
	ouden::CGameMgr::getInstance()->getGameWorld()->update();
	
	//std::cout << "Tick time " << pTimer->getElapsed() << std::endl;
	
	
	ouden::CModelEntity* pModel1 = static_cast<ouden::CModelEntity*>(ouden::CGameMgr::getInstance()->getGameWorld()->findEntity("Model1"));
	
	//opal::Quaternion q = pTheBox->getRootBit()->getSolid()->getQuaternion();
	//Ogre::Quaternion p = pTheBox->getRootBit()->getNode()->_getDerivedOrientation();
	
	return true;
	
}

bool CPlayState::onFrameStart(ouden::CGameTimer* pTimer)
{
	
	
	return true;
	
}

bool CPlayState::frameEnded( )
{
	
	if (m_bExit)
		return false;
		
	return true;
	
}
